home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / Wave / WavesWorld / Source / IBPalettes / WW3DKit / RIBCommandList.m < prev    next >
Encoding:
Text File  |  1995-04-10  |  17.2 KB  |  720 lines

  1. // copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
  2. // see COPYRIGHT for reuse legalities
  3. //
  4.  
  5. #import "RIBCommandList.h"
  6. #import "RIBCommand.h"
  7.  
  8. #import "WW3DShape.h"
  9. #import "WWSceneClock.h"
  10. #import "WW3DAttributeState.h"
  11. #import "usefulWW3DFunctions.h"
  12.  
  13. @implementation RIBCommandList
  14.  
  15. - init
  16. {
  17.   [super init];
  18.   eveCode = NULL;
  19.   dirtyBoundingBox = TRUE;
  20.  
  21.   return self;
  22.  
  23. }
  24.  
  25. - awake
  26. {
  27.   [super awake];
  28.  
  29.   eveCode = NULL;
  30.   dirtyBoundingBox = TRUE;
  31.  
  32.   return self;
  33. }
  34.  
  35. - free
  36. {
  37.   //NXLogError("RIBCommandList %d being free'ed\n", self);
  38.   //NXLogError("RIBCommandList: freeing %p", self);
  39.   [self freeObjects];  
  40.   if (eveCode)
  41.   {  free(eveCode);
  42.   }
  43.   return [super free];
  44. }
  45.  
  46. // override all list insertion and deletion routines to mark myself dirty
  47. - addObject:anObject { dirtyBoundingBox = TRUE; return [super addObject:anObject]; }
  48. - insertObject:anObject at:(unsigned)index { dirtyBoundingBox = TRUE; return [super insertObject:anObject at:(unsigned)index]; }
  49. - removeObjectAt:(unsigned)index { dirtyBoundingBox = TRUE; return [super removeObjectAt:(unsigned)index]; }
  50. - removeLastObject { dirtyBoundingBox = TRUE; return [super removeLastObject]; }
  51. - replaceObjectAt:(unsigned)index with:newObject { dirtyBoundingBox = TRUE; return [super replaceObjectAt:(unsigned)index with:newObject]; }
  52. - appendList: (List *)otherList { dirtyBoundingBox = TRUE; return [super appendList: (List *)otherList]; }
  53. - addObjectIfAbsent:anObject { dirtyBoundingBox = TRUE; return [super addObjectIfAbsent:anObject]; }
  54. - removeObject:anObject  { dirtyBoundingBox = TRUE; return [super removeObject:anObject]; }
  55. - replaceObject:anObject with:newObject  { dirtyBoundingBox = TRUE; return [super replaceObject:anObject with:newObject]; }
  56. - empty  { dirtyBoundingBox = TRUE; return [super empty]; }
  57.  
  58. - (const char *)eveCode { return eveCode; }
  59. - setEveCode:(char *)newEveCode
  60. {
  61.   if (eveCode)
  62.   {  free(eveCode);
  63.   }
  64.   if (newEveCode)
  65.   {  eveCode = NXCopyStringBuffer(newEveCode);
  66.   } 
  67.   else
  68.   {  eveCode = NULL;
  69.   } 
  70.   return self;
  71. }
  72.  
  73. - _setEveCode:(char *)newEveCode
  74. {
  75.   if (newEveCode)
  76.   {  eveCode = NXCopyStringBuffer(newEveCode);
  77.   } 
  78.   else
  79.   {  eveCode = NULL;
  80.   } 
  81.   return self;
  82. }
  83.  
  84. - setEveCodeArgc:(int)argc argv:(char **)argv
  85. {
  86.   int   i, howMany = 1;
  87.   char  *tmpEveCode, *ptr;
  88.  
  89.  
  90.   for (i = 0; i < argc; i++)
  91.   {  howMany += 3 + strlen(argv[i]);
  92.   }
  93.   ptr = tmpEveCode = (char *)calloc(howMany, 1);
  94.  
  95.   sprintf(ptr, "%s ", argv[0]);
  96.   ptr += strlen(argv[0]) + 1;
  97.  
  98.   for (i = 1; i < argc; i++)
  99.   {  sprintf(ptr, "{%s} ", argv[i]);
  100.      ptr += strlen(argv[i]) + 3;
  101.   }
  102.  
  103.   [self setEveCode:tmpEveCode];
  104.  
  105.   free(tmpEveCode);
  106.  
  107.   return self;
  108. }
  109.  
  110. // okay, usually "copy" doesn't actually do a deep copy.
  111. // but, in the case of this subclass, we're gonna.
  112. - copyFromZone:(NXZone *)zone
  113. {
  114.   id   newCopy = [super copyFromZone:zone];
  115.   int  i, howMany = [self count];
  116.   
  117.  
  118.   [newCopy _setEveCode:eveCode];
  119.   for (i = 0; i < howMany; i++)
  120.   {  [newCopy replaceObjectAt:i with:[[self objectAt:i] copyFromZone:zone]];
  121.   }
  122.   return newCopy;
  123. }
  124.  
  125.  
  126. // need to walk down my RIBCommands, asking each one...
  127. - (BOOL)hasBoundingBox 
  128. {  
  129.   int   howMany, i = 0;
  130.   BOOL  found = NO;
  131.  
  132.  
  133.   howMany = [self count];
  134.   while (!found && (i < howMany))
  135.   {  if ([[self objectAt:i] hasBoundingBox])
  136.      {  found = YES;
  137.      }
  138.      i++;
  139.   }
  140.   return found;
  141. }
  142.  
  143. - (BOOL)isLerpable { return YES; }
  144.  
  145. // need to walk down my RIBCommands, asking each one...
  146. - (BOOL)pushesCTM
  147. {  
  148.   int   howMany, i = 0;
  149.   int   pushes = 0, 
  150.         pops = 0;
  151.  
  152.  
  153.   howMany = [self count];
  154.   for (i = 0; i < howMany; i++)
  155.   {  if ([[self objectAt:i] pushesCTM])
  156.      {  pushes++; 
  157.      }
  158.      if ([[self objectAt:i] popsCTM])
  159.      {  pops++; 
  160.      }
  161.   }
  162.   if (pushes > pops)
  163.   {  return YES;
  164.   }
  165.   return NO;
  166. }
  167.  
  168. - (BOOL)popsCTM
  169. {  
  170.   int   howMany, i = 0;
  171.   int   pushes = 0, 
  172.         pops = 0;
  173.  
  174.  
  175.   howMany = [self count];
  176.   for (i = 0; i < howMany; i++)
  177.   {  if ([[self objectAt:i] pushesCTM])
  178.      {  pushes++; 
  179.      }
  180.      if ([[self objectAt:i] popsCTM])
  181.      {  pops++; 
  182.      }
  183.   }
  184.   if (pops > pushes)
  185.   {  return YES;
  186.   }
  187.   return NO;
  188. }
  189.  
  190. - (BOOL)pushesOrPopsCTM
  191. {  
  192.   int   howMany, i = 0;
  193.   int   pushes = 0, 
  194.         pops = 0;
  195.  
  196.  
  197.   howMany = [self count];
  198.   for (i = 0; i < howMany; i++)
  199.   {  if ([[self objectAt:i] pushesCTM])
  200.      {  pushes++; 
  201.      }
  202.      if ([[self objectAt:i] popsCTM])
  203.      {  pops++; 
  204.      }
  205.   }
  206.   if (pushes != pops)
  207.   {  return YES;
  208.   }
  209.   return NO;
  210. }
  211.  
  212. - (float)lastSampleIsAt { return 0.0; }
  213.  
  214.  
  215. - (unsigned long int)maxSampleBandwidth
  216. {
  217.    unsigned long int  maxSampleBandwidth = 50;  //WAVE FIX ME
  218.    int      i, howMany = [self count];
  219.  
  220.  
  221.   for (i = 0; i < howMany; i++)
  222.   {  maxSampleBandwidth += [(id <WWRenderable>)[self objectAt:i] maxSampleBandwidth];
  223.   }
  224.  
  225.   return maxSampleBandwidth;
  226. }
  227.  
  228.  
  229. // need to walk down my RIBCommands, asking each one...
  230. - (BOOL)isMotionBlurrable 
  231. {  
  232.   int   howMany, i = 0;
  233.   BOOL  found = NO;
  234.  
  235.  
  236.   howMany = [self count];
  237.   while (!found && (i < howMany))
  238.   {  if ([[self objectAt:i] isMotionBlurrable])
  239.      {  found = YES;
  240.      }
  241.      i++;
  242.   }
  243.   return found;
  244. }
  245.  
  246. - (BOOL)isCompoundCommand { return YES; }
  247.  
  248.  
  249. // need to walk down my RIBCommands, asking each one...
  250. // no, unfortunately, it's not as easy as that.  
  251. // This is actually much more like a WW3DShape.  
  252. // we might have transformative commands inside the list.
  253. // what if we had a RIBScale and a RIBSphere object?
  254. // we need to keep a notion of a CTM inside the list, and
  255. // apply it to both the bounding box thus far and the current
  256. // command
  257. // NOTE: the changes haven't been made yet...
  258.  
  259. - (int)findBoundingBoxFromRIBCommands:(int)startingIndex using:attributeState startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  260. {
  261.   RtBound   tmpBoundingBox;
  262.   RtMatrix  tmpCTM;
  263.   int       i = startingIndex,
  264.             incr,
  265.             howMany = [self count];
  266.   id        cmd;
  267.   id        newAttributeState;
  268.  
  269.  
  270.   // first we need to find a bounding box to start with
  271.   while (i < howMany)
  272.   {  cmd = [self objectAt:i];
  273.  
  274.      // if the cmd pops the attribute stack, we're done.
  275.      // we return how many commands we've gone through in our time here
  276.      if ([cmd popsCTM])
  277.      {  i++;
  278.         return (i - startingIndex);
  279.      }
  280.  
  281.      if ([cmd pushesCTM])  // here we go again...
  282.      {  newAttributeState = [[WW3DAttributeState alloc] init];
  283.         i++;
  284.         incr = [self findBoundingBoxFromRIBCommands:i using:newAttributeState startingAt:shutterOpenTime endingAt:shutterCloseTime];
  285.         // we now need to merge this sub-attribute block into our current one
  286.         if ([newAttributeState hasBoundingBox])
  287.         {  // This means that the bounding box in newAttributeState needs to be
  288.            // transformed by the ctm of attributeState, and then  we need to 
  289.            // "grow" to the current bound with that tranformed sub-bound.
  290.  
  291.            [attributeState getTransformMatrix:tmpCTM];
  292.           WW3DDetermineBoundGivenBoundAndCTM(&tmpBoundingBox, [newAttributeState boundingBox], tmpCTM);
  293.           [attributeState growBoundingBox:&tmpBoundingBox];
  294.        }
  295.        else
  296.        {  NXLogError("warning: RIBCommandList <%s> has a moot set of commands at indices %d to %d\n", [myShape shapeName], (i - 1), (i + incr - 1));
  297.           NXLogError("\tif there was a LightSource or AreaLight source in there, you can ignore this warning, though...\n");
  298.        }
  299.        i += incr;
  300.      }
  301.      else 
  302.      {  [cmd transformCTM:attributeState startingAt:shutterOpenTime endingAt:shutterCloseTime];
  303.         i++;
  304.      }
  305.  
  306.     if ([cmd hasBoundingBox])
  307.     {  // great! we grow the bound of the current attribute state
  308.        [attributeState growBoundingBox:[cmd boundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime]];
  309.     }
  310.  
  311.   }
  312.   return (i - startingIndex);
  313. }
  314.  
  315.  
  316.  
  317. - calculateBoundingBoxStartingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime  
  318. {  
  319.   int  i;
  320.   id   newAttributeState;
  321.  
  322.  
  323.   if (![self hasBoundingBox])
  324.   {  return self;  // I don't have a bounding box!
  325.   }
  326.  
  327.   // I have a bounding box; but I'm not sure what to set it to
  328.   // initially...  I'll use the bounding box of the first 
  329.   // RIBCommand I have that has a bounding box.
  330.   newAttributeState = [[WW3DAttributeState alloc] init];
  331.   i = [self findBoundingBoxFromRIBCommands:0 using:newAttributeState startingAt:shutterOpenTime endingAt:shutterCloseTime];
  332.   if (i != [self count])
  333.   {  NXLogError("warning: RIBCommandList in shape <%s> has an unbalanced attribute delimiter at command %d (expected %d)\n", 
  334.                 [myShape shapeName], i, [self count]);
  335.      return nil;
  336.   }
  337.  
  338.   if ([newAttributeState hasBoundingBox])
  339.   {  N3D_CopyBound(*([newAttributeState boundingBox]), boundingBox);
  340.   }
  341.   else
  342.   {  NXLogError("warning: RIBCommandList in shape <%s> thought it had a bounding box, but it seems it doesn't...\n",
  343.                 [myShape shapeName]);
  344.      return nil;
  345.   }
  346.   return self;
  347. }
  348.  
  349.  
  350. - setBoundingBox:(RtBound *)newBoundingBox
  351. {
  352.   N3D_CopyBound(*newBoundingBox, boundingBox);
  353.  
  354.   return self;
  355. }
  356.  
  357. - (RtBound *)boundingBoxStartingAt:(RtFloat)intervalStartTime endingAt:(RtFloat)intervalEndTime
  358.    if (dirtyBoundingBox) 
  359.    {  [self calculateBoundingBoxStartingAt:intervalStartTime endingAt:intervalEndTime];
  360.    }
  361.    return &boundingBox; 
  362. }
  363.  
  364. - setMyShape:shape
  365. {
  366.   int   howMany = [self count], 
  367.         i = 0;
  368.  
  369.  
  370.   myShape = shape;  
  371.   for (i = 0; i < howMany; i++)
  372.   {  [(id <WWRenderable>)[self objectAt:i] setMyShape:myShape];
  373.   }
  374.   return self; 
  375. }
  376.  
  377. - shape { return myShape; }
  378.  
  379. - (unsigned long int)dataBandwidth
  380. {
  381.    int   howMany = [self count], 
  382.          i = 0;
  383.    unsigned 
  384.        long int dataBandwidth = 0;
  385.  
  386.  
  387.   for (i = 0; i < howMany; i++)
  388.   {  dataBandwidth += [(id <WWRenderable>)[self objectAt:i] dataBandwidth];
  389.   }
  390.   
  391.   return dataBandwidth;
  392. }
  393.  
  394.  
  395. // note: because we've made the WWSampleList "safe" for having
  396. // multiple samples with the same data, it's perfectly valid to return
  397. // yourself or b
  398. - lerpWith:b by:(float)uValue
  399. {
  400.    id  newMe = nil, newElement;
  401.    int i, howMany = [self count];
  402.  
  403.  
  404.    if (([self class] != [b class]) || (uValue <= 0.0) || (howMany != [b count]))
  405.    {  return self;
  406.    }
  407.  
  408.    if (uValue >= 1.0)
  409.    {  return b;
  410.    }
  411.  
  412.    newMe = [self copyFromZone:[self zone]];
  413.    [newMe empty];
  414.  
  415.    for (i = 0; i < howMany; i++)
  416.    {  newElement = [[self objectAt:i] copyFromZone:[self zone]];
  417.       if ([newElement isLerpable])
  418.       {  [newElement lerpSelfWith:[b objectAt:i] by:uValue];
  419.       }
  420.       [newMe insertObject:newElement at:i];
  421.    }
  422.  
  423.    return newMe;
  424. }
  425.  
  426. - lerpSelfWith:b by:(float)uValue
  427. {
  428.    id  element;
  429.    int i, howMany = [self count];
  430.  
  431.  
  432.    if (([self class] != [b class]) || (uValue <= 0.0) || (howMany != [b count]))
  433.    {  return self;
  434.    }
  435.  
  436.    if (uValue >= 1.0)
  437.    {  return b;
  438.    }
  439.  
  440.    for (i = 0; i < howMany; i++)
  441.    {  element = [self objectAt:i];
  442.       [element lerpSelfWith:[b objectAt:i] by:uValue];
  443.    }
  444.  
  445.    return self;
  446. }
  447.  
  448.  
  449. - renderMaps:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime usingStream:(NXStream *)ns
  450. {
  451.   int   howMany = [self count], 
  452.         i = 0;
  453.  
  454.  
  455.   for (i = 0; i < howMany; i++)
  456.   {  [(id <WWRenderable>)[self objectAt:i] renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  457.   }
  458.   
  459.   return self;
  460. }
  461.  
  462. - renderMaps:(WW3DCamera *)camera usingStream:(NXStream *)ns
  463. {
  464.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  465.            shutterCloseTime = [camera shutterCloseTime];
  466.  
  467.  
  468.   return [self renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:ns];
  469. }
  470.  
  471. - renderMaps:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  472. {
  473.   int   howMany = [self count], 
  474.         i = 0;
  475.  
  476.  
  477.   for (i = 0; i < howMany; i++)
  478.   {  [(id <WWRenderable>)[self objectAt:i] renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  479.   }
  480.   
  481.   return self;
  482. }
  483.  
  484. - renderMaps:(WW3DCamera *)camera
  485. {
  486.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  487.            shutterCloseTime = [camera shutterCloseTime];
  488.  
  489.  
  490.   return [self renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  491. }
  492.  
  493. - renderSelfAsBox:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  494. {
  495.   int   howMany = [self count], 
  496.         i = 0;
  497.  
  498.  
  499.   for (i = 0; i < howMany; i++)
  500.   {  [(id <WWRenderable>)[self objectAt:i] renderSelfAsBox:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  501.   }
  502.   
  503.   return self;
  504. }
  505.  
  506. - renderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  507. {
  508.   int                howMany = [self count], 
  509.                      i = 0;
  510.   id <WWRenderable>  obj;
  511.  
  512.  
  513.   if (NXDrawingStatus == NX_DRAWING) 
  514.   {  for (i = 0; i < howMany; i++)
  515.      {  [(id <WWRenderable>)[self objectAt:i] renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  516.      }
  517.   }
  518.   else
  519.   {  for (i = 0; i < howMany; i++) // do the moot check
  520.      {  obj = (id <WWRenderable>)[self objectAt:i];
  521.         if (![obj isMoot])
  522.         {  [obj renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  523.         }
  524.      }
  525.   }
  526.  
  527.   return self;
  528. }
  529.  
  530. - renderSelf:(WW3DCamera *)camera
  531. {
  532.   RtFloat  shutterOpenTime = [camera shutterOpenTime],
  533.            shutterCloseTime = [camera shutterCloseTime];
  534.  
  535.  
  536.   return [self renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  537. }
  538.  
  539. - preRenderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  540. {
  541.   return [self preRenderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
  542. }
  543.  
  544. - preRenderSelf:(WW3DCamera *)camera 
  545. {
  546.   return [self renderSelf:camera];
  547. }
  548.  
  549.  
  550. - (int)spinThroughSubAttributeBlocks:(int)startingIndex
  551. {
  552.   int       i = startingIndex,
  553.             howMany = [self count];
  554.   id        cmd;
  555.  
  556.  
  557.   while (i < howMany)
  558.   {  cmd = [self objectAt:i];
  559.  
  560.      // if the cmd pops the attribute stack, we're done.
  561.      // we return how many commands we've gone through in our time here
  562.      if ([cmd popsCTM])
  563.      {  i++;
  564.         return (i - startingIndex);
  565.      }
  566.  
  567.      if ([cmd pushesCTM])  // here we go again...
  568.      {  i++;
  569.         i += [self spinThroughSubAttributeBlocks:i];
  570.      }
  571.      else 
  572.      {  i++;
  573.      }
  574.   }
  575.   return (i - startingIndex);
  576. }
  577.  
  578. - transformCTM:(WW3DAttributeState *)attributeState startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
  579.   int   howMany = [self count], 
  580.         i = 0;
  581.   id    cmd;
  582.  
  583.  
  584.   while (i < howMany)
  585.   {  cmd = [self objectAt:i];
  586.      if ([cmd pushesCTM])
  587.      {  //okay, recursively spin until we pop or finish...
  588.         i++;
  589.         i += [self spinThroughSubAttributeBlocks:0];
  590.      }
  591.      else
  592.      {  [(id <WWRenderable>)[self objectAt:i++] transformCTM:attributeState startingAt:shutterOpenTime endingAt:shutterCloseTime]; 
  593.      }
  594.   }
  595.   
  596.   return self;
  597.  
  598. }
  599.  
  600. // WavesWorld archiving:
  601. // writeEve:(NXStream *)stream
  602. // writeScene:(NXStream *)stream
  603.  
  604. - oldWriteEve:(NXStream *)stream atTabLevel:(int)tab
  605. {
  606.    int   howMany = [self count], 
  607.          i;
  608.  
  609.  
  610.    for (i = 0; i < howMany; i++)
  611.    {  [(id <WWRenderable>)[self objectAt:i] writeEve:stream atTabLevel:tab];
  612.    }
  613.    return self;
  614. }
  615.  
  616. - writeEve:(NXStream *)stream atTabLevel:(int)tab
  617. {
  618.    int  i;
  619.  
  620.    for (i = 0; i < tab; i++)
  621.    {  NXPrintf(stream, "\t");
  622.    }
  623.    if (!eveCode)
  624.    {  NXPrintf(stream, "# this RIBCommandList had no eveCode...");
  625.    }
  626.    else
  627.    {  NXPrintf(stream, "%s", eveCode);
  628.    }
  629.  
  630.    return self;
  631. }
  632.  
  633. - writeScene:(NXStream *)stream atTabLevel:(int)tab
  634. {
  635.    return [self writeEve:stream atTabLevel:tab];
  636. }
  637.  
  638. - write3DTextScene:(NXStream *)stream atTabLevel:(int)tab index:(int)index time:(float)time until:(float)lastTime
  639. {
  640.    int   howMany = [self count], 
  641.          i;
  642.  
  643.  
  644.    for (i = 0; i < howMany; i++)
  645.    {  [(id <WWRenderable>)[self objectAt:i] write3DTextScene:stream atTabLevel:tab index:index++ time:time until:lastTime];
  646.       // really need to be moving the X coordinate by the X bound of each, and make it cumulative...
  647.    }
  648.    return self;
  649. }
  650.  
  651. - writeInventorAtTime:(float)currentTime to:(NXStream *)stream atTabLevel:(int)tab
  652. {
  653.    int                howMany = [self count], 
  654.                       i, newTab = tab + 1;
  655.    id <WWRenderable>  aCmd;
  656.  
  657.  
  658.    for (i = 0; i < tab; i++)
  659.    {  NXPrintf(stream, "\t");
  660.    }
  661.    NXPrintf(stream, "# RIBCommandList:\n");
  662.    for (i = 0; i < (howMany-1); i++)
  663.    {  aCmd = [self objectAt:i];
  664.       if (![aCmd isMootStartingAt:currentTime endingAt:currentTime])
  665.       {  if ([aCmd popsCTM])
  666.          {  newTab--;
  667.          }
  668.          [aCmd writeInventorAtTime:currentTime to:stream atTabLevel:newTab];
  669.          if ([aCmd pushesCTM])
  670.          {  newTab++;
  671.          }
  672.          NXPrintf(stream, "\n");
  673.       }
  674.    }
  675.  
  676.    aCmd = [self objectAt:i];
  677.    if (![aCmd isMootStartingAt:currentTime endingAt:currentTime])
  678.    {  [(id <WWRenderable>)[self objectAt:i] writeInventorAtTime:currentTime to:stream atTabLevel:newTab];
  679.    }
  680.    return self;
  681. }
  682.  
  683. - (BOOL)theSameAs:otherRIBCommand
  684. {
  685.   // make the default NO...  
  686.   return NO;
  687. }
  688.  
  689. - (BOOL)similarTo:otherRIBCommand 
  690. {
  691.   if ([self class] != [otherRIBCommand class])
  692.   {  return NO;
  693.   }
  694.   return YES;
  695. }
  696.  
  697.  
  698. - (BOOL)isMoot
  699. {
  700.    int  i, howMany = [self count];
  701.  
  702.  
  703.    for (i = 0; i < howMany; i++)
  704.    {  if (![(id <WWRenderable>)[self objectAt:i] isMoot])
  705.       {  return NO;
  706.       }
  707.    }
  708.    // if there are no commands, or they're all moot, then so am i...
  709.    return YES;
  710. }
  711.  
  712. - (BOOL)isMootStartingAt:(float)startTime endingAt:(float)endTime  {  return [self isMoot];  }
  713.  
  714. // boy, this is dumb... This is to get around the stupid warnings from the compiler - ask wave for details
  715. - class { return [super class]; }
  716.  
  717. @end
  718.